package ch.iec.tc57._2011.meterreadschedules.clientsample;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.ws.Holder;

import ch.iec.tc57._2011.meterreadschedules.EndDeviceAsset;
import ch.iec.tc57._2011.meterreadschedules.ExecuteMeterReadSchedules;
import ch.iec.tc57._2011.meterreadschedules.FaultMessage;
import ch.iec.tc57._2011.meterreadschedules.MeterReadSchedules;
import ch.iec.tc57._2011.meterreadschedules.MeterReadSchedulesPort;
import ch.iec.tc57._2011.meterreadschedules.TimeSchedule;
import ch.iec.tc57._2011.meterreadschedules.TimeSchedule.ScheduleInterval;
import ch.iec.tc57._2011.meterreadschedules.TimeSchedule.TimePoints;
import ch.iec.tc57._2011.meterreadschedulesmessage.MeterReadSchedulesPayloadType;
import ch.iec.tc57._2011.schema.message.HeaderType;
import ch.iec.tc57._2011.schema.message.MessageProperty;
import ch.iec.tc57._2011.schema.message.ReplyType;
import ch.iec.tc57._2011.schema.message.RequestType;
import ch.iec.tc57._2011.schema.message.UserType;

public class ClientSample {

	public static void main(String[] args) {
		System.out.println("***********************");
		System.out.println("Create Web Service Client...");
		ExecuteMeterReadSchedules service1 = new ExecuteMeterReadSchedules();
		System.out.println("Create Web Service...");
		MeterReadSchedulesPort port1 = service1.getMeterReadSchedulesPort();
		System.out.println("Call Web Service Operation...");
		System.out.println("Server said: port1.changeMeterReadSchedules() is a void method!");
		System.out.println("Server said: port1.cancelMeterReadSchedules() is a void method!");
		System.out.println("Server said: port1.createMeterReadSchedules() is a void method!");
		System.out.println("Server said: port1.deleteMeterReadSchedules() is a void method!");
		System.out.println("Server said: port1.closeMeterReadSchedules() is a void method!");
		System.out.println("Create Web Service...");
		MeterReadSchedulesPort port2 = service1.getMeterReadSchedulesPort();
		System.out.println("Call Web Service Operation...");
		System.out.println("Server said: port2.changeMeterReadSchedules() is a void method!");
		System.out.println("Server said: port2.cancelMeterReadSchedules() is a void method!");
		System.out.println("Server said: port2.createMeterReadSchedules() is a void method!");
		System.out.println("Server said: port2.deleteMeterReadSchedules() is a void method!");
		System.out.println("Server said: port2.closeMeterReadSchedules() is a void method!");
		System.out.println("***********************");
		System.out.println("Call Over!");
	}

	public static String doRequestWithoutTicket(MeterReadSchedulesPort port1, 
			List<String> endDeviceAssets, String recurrencePattern, int retries, int retrytime,
			int yearIni, int monthIni, int dayIni) throws FaultMessage {
		String ticket = null;
		try {
			Holder<HeaderType> header = new Holder<HeaderType>();
			header.value = constructExecMeterReadSchedulesHeader();

			RequestType request = new RequestType();

			Holder<MeterReadSchedulesPayloadType> payload = new Holder<MeterReadSchedulesPayloadType>();
			MeterReadSchedulesPayloadType value = new MeterReadSchedulesPayloadType();
			MeterReadSchedules meterReadSchedules = new MeterReadSchedules();

			EndDeviceAsset endDeviceAsset;
			for (String mrid : endDeviceAssets) {
				endDeviceAsset = new EndDeviceAsset();
				endDeviceAsset.setMRID(mrid);
				meterReadSchedules.getEndDeviceAsset().add(endDeviceAsset);	
			}

			TimeSchedule timeSchedule = new TimeSchedule();
			timeSchedule.setRecurrencePattern(recurrencePattern);
			ScheduleInterval scheduleInterval = new ScheduleInterval();
			scheduleInterval.setStart(getXMLGregorianCalendarFromGregorianCalendar(new GregorianCalendar(yearIni, monthIni, dayIni)));
			timeSchedule.setScheduleInterval(scheduleInterval);

			if (retries > 0 && retrytime > 0) {
				TimePoints timePoint;
				for (int i = 0; i < retries; i++) {
					timePoint = new TimePoints();
					timePoint.setRelativeTimeinterval(0f);
					timeSchedule.getTimePoints().add(timePoint);
				}
				timeSchedule.setRecurrencePeriod(Float.valueOf(retrytime));	
			}
			meterReadSchedules.getTimeSchedule().add(timeSchedule);

			value.setMeterReadSchedules(meterReadSchedules);
			payload.value = value;

			Holder<ReplyType> reply = new Holder<ReplyType>();

			port1.createMeterReadSchedules(header, request, payload, reply);

			if( reply.value != null ) {
				System.out.println("--- llego reply: "+reply.value.getResult());					
			}

			if( header.value != null ) {
				ticket = extractTicket(header);
			}

			//printPayload(payload);

		} catch (FaultMessage e) {
			e.printStackTrace();
			throw e;
		}
		return ticket;
	}

	public static String doRequestWithTicket(String ticket, MeterReadSchedulesPort port1, 
			List<String> endDeviceAssets, String recurrencePattern, int retries, int retrytime,
			int yearIni, int monthIni, int dayIni) throws FaultMessage {
		boolean done = false;
		String res = null;
		try {
			Holder<HeaderType> header = new Holder<HeaderType>();
			header.value = composeHeaderType(ticket);

			RequestType request = new RequestType();

			Holder<MeterReadSchedulesPayloadType> payload = new Holder<MeterReadSchedulesPayloadType>();
			MeterReadSchedulesPayloadType value = new MeterReadSchedulesPayloadType();
			MeterReadSchedules meterReadSchedules = new MeterReadSchedules();

			EndDeviceAsset endDeviceAsset;
			for (String mrid : endDeviceAssets) {
				endDeviceAsset = new EndDeviceAsset();
				endDeviceAsset.setMRID(mrid);
				meterReadSchedules.getEndDeviceAsset().add(endDeviceAsset);	
			}

			TimeSchedule timeSchedule = new TimeSchedule();
			timeSchedule.setRecurrencePattern(recurrencePattern);
			ScheduleInterval scheduleInterval = new ScheduleInterval();
			scheduleInterval.setStart(getXMLGregorianCalendarFromGregorianCalendar(new GregorianCalendar(yearIni, monthIni, dayIni)));
			timeSchedule.setScheduleInterval(scheduleInterval);

			if (retries > 0 && retrytime > 0) {
				TimePoints timePoint;
				for (int i = 0; i < retries; i++) {
					timePoint = new TimePoints();
					timePoint.setRelativeTimeinterval(0f);
					timeSchedule.getTimePoints().add(timePoint);
				}
				timeSchedule.setRecurrencePeriod(Float.valueOf(retrytime));	
			}
			meterReadSchedules.getTimeSchedule().add(timeSchedule);

			value.setMeterReadSchedules(meterReadSchedules);
			payload.value = value;

			Holder<ReplyType> reply = new Holder<ReplyType>();

			port1.createMeterReadSchedules(header, request, payload, reply);

			if( reply.value != null ) {
				System.out.println("--- llego reply: "+reply.value.getResult());					
			}

			res = printReply(reply);

		} catch (FaultMessage e) {
			e.printStackTrace();
			throw e;
		}
		return res;
	}
	
	private static String printReply(Holder<ReplyType> reply) {
		StringBuilder builder = new StringBuilder();
		if (reply.value != null) {
			ReplyType value = reply.value;
			List<Object> any = value.getAny();
			if (any != null && any.size() > 0) {
				Map<String, ArrayList<ScheduleOperationResult>> map = (Map<String, ArrayList<ScheduleOperationResult>>)any.get(0);
				Set<String> keySet = map.keySet();
				ArrayList<ScheduleOperationResult> arList;
				for (String idDevice : keySet) {
					builder.append("idDevice: " + idDevice);
					arList = map.get(idDevice);
					for (ScheduleOperationResult sor : arList) {
						builder.append(" ScheduleOperationResult: " + sor);	
					}
				}
				
				ListResult listResult = (ListResult) any.get(1);
				List<ScheduleOperationResult> failedSchedules = listResult.getFailedSchedules();
				for (ScheduleOperationResult sor : failedSchedules) {
					builder.append("Failed: " + sor);
				}	
			}
		}
		System.out.println("BUILDER: " + builder);
		return builder.toString().length() == 0 ? null : builder.toString();
	}

	private static HeaderType constructExecMeterReadSchedulesHeader() {
		HeaderType headerType = new HeaderType();
		headerType.setVerb("create");
		headerType.setNoun("MeterReadSchedule");
		headerType.setRevision("1");
		headerType.setContext("Testing");

		headerType.setTimestamp(getXMLGregorianCalendarFromGregorianCalendar());

		headerType.setSource("PrimeStone");
		headerType.setMessageID(nextString());

		UserType userType = new UserType();
		userType.setUserID("Test");
		userType.setOrganization("Pstone");

		headerType.setUser(userType);

		return headerType;
	}

	/**
	 * 
	 * @return
	 */
	private static XMLGregorianCalendar getXMLGregorianCalendarFromGregorianCalendar() {
		//		String nowString;
		//		GregorianCalendar nowGregorianCalendar = null;
		//		try {
		//			nowString = ISO8601.fromCalendar(Calendar.getInstance());
		//			nowGregorianCalendar = ISO8601.toGregorianCalendar(nowString);
		//		} catch (ParseException e) {
		//			e.printStackTrace();
		//		}

		XMLGregorianCalendar xmlGregorianCalendar = null;
		try {
			//xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(nowGregorianCalendar);
			xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar());
		} catch (DatatypeConfigurationException e) {
			e.printStackTrace();
		}
		return xmlGregorianCalendar;
	}

	/**
	 * 
	 * @param gregorianCalendar
	 * @return
	 */
	private static XMLGregorianCalendar getXMLGregorianCalendarFromGregorianCalendar(GregorianCalendar gregorianCalendar) {
		//		String nowString;
		//		GregorianCalendar nowGregorianCalendar = null;
		//		try {
		//			nowString = ISO8601.fromCalendar(Calendar.getInstance());
		//			nowGregorianCalendar = ISO8601.toGregorianCalendar(nowString);
		//		} catch (ParseException e) {
		//			e.printStackTrace();
		//		}

		XMLGregorianCalendar xmlGregorianCalendar = null;
		try {
			//xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(nowGregorianCalendar);
			xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
		} catch (DatatypeConfigurationException e) {
			e.printStackTrace();
		}
		return xmlGregorianCalendar;
	}

	private static double getGaussian(double aMean, double aVariance){
		return aMean + new Random().nextGaussian() * aVariance;
	}

	private static double nextDouble() {
		return getGaussian(1000.0f, 500.0f);
	}

	private static String nextString() {
		return String.valueOf(getGaussian(1000.0f, 500.0f));
	}

	private static String nextString(double mean, double variance) {
		return String.valueOf(getGaussian(mean, variance));
	}

	private static String extractTicket(Holder<HeaderType> header) {
		if (header.value != null) {
			List<MessageProperty> properties = header.value.getProperty();
			for (MessageProperty messageProperty : properties) {
				if(messageProperty.getName().equals("ticket")) {
					return messageProperty.getValue();
				}
			}
		}
		return null;
	}

	private static HeaderType composeHeaderType(String ticket) {
		HeaderType headerType = constructExecMeterReadSchedulesHeader();
		MessageProperty mpt = new MessageProperty();
		mpt.setName("ticket");
		mpt.setValue(ticket); //1526.076900399347 //716.7600952815999 //596.127408328624
		headerType.getProperty().add(mpt);
		return headerType;
	}
}
